#pragma once
#include <windows.h>
#include <wincrypt.h>
#using <system.dll>


/// Bitmask to remove the revocation status from the results of a chain building operation
#define REMOVE_REVOCATION_STATUS_MASK (~(CERT_TRUST_IS_REVOKED | CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
/// Bitmask to remove errors from the chain building operation which are not defined in the
/// RFC
#define REMOVE_NONSTANDARD_ERRORS_MASK (~(CERT_TRUST_IS_NOT_TIME_NESTED))
/// Bitmask to remove time validity errors from the chain building operation.  This is necessary
/// since CertGetCertificateChain cannot build a certificate chain for a certificate that is no
/// longer time-valid, even if it was valid at the time of signature.
#define REMOVE_TIMEVALID_ERROR_MASK (~(CERT_TRUST_IS_NOT_TIME_VALID))



namespace Safe
{
	public ref class CGemsecUtilities
	{
	public:

		/// <summary>
		/// Uses CryptEncodeObject to encode a structure.  Use EncodeFree to release data which 
		/// is allocated.
		/// </summary>
		/// <param name='input'>The input to pass to CryptEncodeObject</param>
		/// <param name='encodeType'>The encoding type to pass to CryptEncodeObject</param>
		/// <param name='output'>An output buffer to hold the result of the CryptEncodeObject 
		/// operation.</param>
		/// <returns>An integer code from errors.h</returns>
		static int Encode(const void * input, LPCSTR encodeType, CRYPT_DATA_BLOB& output);

		/// <summary>Frees data allocated by Encode(), Decode(), CreateBlob()</summary>
		/// <param name='toFree'>A non-null blob containing the data to be freed</param>
		/// <returns>An integer code from errors.h</returns>
		static int FreeMem(CRYPT_DATA_BLOB * toFree);

		/// <summary>
		/// Uses CryptDecodeObject to decode a structure.  Use DecodeFree to release data 
		/// allocated by this function
		/// </summary>
		/// <param name='input'>The blob to pass to CryptDecodeObject</param>
		/// <param name='decodeType'>The type of object contained in the 'input' parameter</param>
		/// <param name='output'>A non-null CRYPT_DATA_BLOB structure to hold the result of the
		/// decode operation</param>
		/// <returns>An integer code from errors.h</returns>
		static int Decode(CRYPT_DATA_BLOB &input, LPCSTR lpDecodeType, CRYPT_DATA_BLOB &output);

		/// <summary>Creates the blob type needed by other functions</summary>
		/// <param name='pbInput'>Buffer containing the data to copy to the blob</param>
		/// <param name='cbInput'>The length of the pbInput buffer</param>
		/// <param name='blob'>The CRYPT_DATA_BLOB to populate</param>
		/// <returns>An integer code from errors.h</returns>
		static int CreateBlob(const BYTE* pbInput, DWORD cbInput, CRYPT_DATA_BLOB &outBlob );

		/// <summary>
		/// Copies the binary part of a blob to a buffer.  This function allocates
		/// memory that needs to be freed by calling FreeVoid.
		/// </summary>
		/// <param name='input'>The CRYPT_DATA_BLOB source</param>
		/// <param name='output'>Pointer to a buffer to receive the copied output</param>
		/// <returns>An integer code from errors.h</returns>
		static int Copy(CRYPT_DATA_BLOB &input, void **output);

		/// <summary>
		/// Frees memory allocated by Copy
		/// </summary>
		/// <param name='toFree'>The buffer to deallocate</param>
		/// <returns>An integer code from errors.h</returns>
		static void FreeVoid(void ** toFree);

		/// <summary>
		/// Returns TRUE if the two passed in BLOBs are equal
		/// </summary>
		/// <returns>An integer code from errors.h</returns>
		static BOOL BlobsAreEqual(CRYPT_DER_BLOB * first, CRYPT_DER_BLOB * second);
	};
}



namespace Safe
{
	public enum class SupportedHashAlgorithm
	{
		MD5, SHA1, SHA256, SHA384, SHA512
	};

	public ref class HashEngine
	{
		private:
			System::Security::Cryptography::HashAlgorithm^ hashAlg;
			System::IO::MemoryStream^ dataStream;

		public:
			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name='alg'>The algorithm used to create the hash</param>
			HashEngine(SupportedHashAlgorithm alg);

			/// <summary>
			/// Destructor
			/// </summary>
			virtual ~HashEngine();

			/// <summary>
			/// Updates the buffer of data to hash with the given array of bytes
			/// </summary>
			/// <param name='data'>An array of bytes to add to the hashed data</param>
			void Update( array<unsigned char>^ data );

			/// <summary>
			/// Updates the buffer of data to hash with the given array of bytes
			/// </summary>
			/// <param name='cbData'>The number of bytes in the pbData parameter</param>
			/// <param name='pbData'>An array of bytes to add to the hashed data</param>
			void Update( BYTE *pbData, DWORD cbData );

			/// <summary>
			/// Hashes the current contents of the data buffer.  Calling this operation
			/// clears the internal buffer and resets it to the initial empty state.
			/// </summary>
			/// <returns>The result of the hash operation</returns>
			array<unsigned char>^ Hash();
	};
}

